/******************************************************************************
 *                                                                            *
 *               (c) 2008-2009 HutLab. All rights reserved.                   *
 *                        Eduardo Henrique Leite                              *
 *                                                                            *
 ******************************************************************************

 History:
 Created 05/01/2009
 Added hint bot 07/01/2009

 */

package hutlab.sudoku.engine.ai;

import hutlab.sudoku.engine.math.*;

/**
 *
 */
public class SudokuBot {

    // ----- CONSTANTS -----

    private static final int SOLVE_CELLS_UNIQUE_VALUE_ROWS = 0;
    private static final int SOLVE_CELLS_UNIQUE_VALUE_COLS = 0;

    // ----- FIELDS -----

    private SudokuPuzzle mPuzzle;

    // ----- GETTERS -----

    public SudokuPuzzle getPuzzle(){
        return mPuzzle;
    }

    // ----- PRIVATE -----

    private boolean solveCellsWithOnePossibility()
    {
        boolean found = false;

        SudokuCell cell;
        for (int i=0; i<this.mPuzzle.getSize();i++){
            for (int j=0; j<this.mPuzzle.getSize(); j++){
                cell = this.mPuzzle.getPuzzle()[i][j];

                if (!cell.validate()){
                    if (cell.getPossibilities().getCount() == 1){
                        this.mPuzzle.setValue(cell.getRow(), cell.getColumn(), cell.getPossibilities().getElement(0));
                        found = true;
                    }
                }
            }
        }

        return found;
    }

    private boolean solveRegionsUniqueValue(){
        boolean changed = false;

        SudokuSearch search = new SudokuSearch();
        SudokuCell cell;
        SudokuSearchItem item;

        int i, j, k, region, possibility;

        for (region=0;region<9;region++){
            search.clear();

            for (i = 0; i< this.mPuzzle.getSize(); i++){
                for (j=0; j<this.mPuzzle.getSize(); j++){
                    if (SudokuHelper.calculateSquare(SudokuHelper.calculateIndex(i,j,9), 9) == region){
                        cell = this.mPuzzle.getPuzzle()[i][j];
                        if (!cell.validate()){
                            for (k=0; k<cell.getPossibilities().getCount();k++){
                                possibility = cell.getPossibilities().getElement(k);
                                if (search.contains(possibility)){
                                    search.update(possibility);
                                }
                                else{
                                    search.add(possibility, cell.getRow(), cell.getColumn());
                                }
                            }
                        }
                    }
                }
            }

            for (j=0; j<search.getItems().getCount();j++){
                item = (SudokuSearchItem) search.getItems().getElement(j);

                if(item.getCount() == 1){
                    changed = true;
                    this.getPuzzle().setValue(item.getRow(), item.getColumn(), item.getValue());
                }
            }
        }

        return changed;
    }

    private boolean solveCellsUniqueValue(int options){
        boolean changed = false;

        SudokuSearch search = new SudokuSearch();
        SudokuCell cell;
        SudokuSearchItem item;

        int j, k, possibility;

        for (int i = 0; i < this.mPuzzle.getSize(); i++) {
            search.clear();

            for (j = 0; j < this.mPuzzle.getSize(); j++) {
                cell = (options == SOLVE_CELLS_UNIQUE_VALUE_ROWS ?
                        this.mPuzzle.getPuzzle()[i][j]: this.mPuzzle.getPuzzle()[j][i]);
                if (! cell.validate()) {
                    for(k=0; k<cell.getPossibilities().getCount(); k++){
                        possibility = cell.getPossibilities().getElement(k);
                        if (search.contains(possibility)){
                            search.update(possibility);
                        }
                        else {
                            search.add(possibility, cell.getRow(), cell.getColumn());
                        }
                    }

                }
            }

            for (j=0; j<search.getItems().getCount();j++){
                item = (SudokuSearchItem) search.getItems().getElement(j);

                if(item.getCount() == 1){
                    changed = true;
                    this.getPuzzle().setValue(item.getRow(), item.getColumn(), item.getValue());
                }
            }
        }

        return changed;
    }

    private boolean solveColsUniqueValue(){
        return solveCellsUniqueValue(SOLVE_CELLS_UNIQUE_VALUE_COLS);
    }

    private boolean solveRowsUniqueValue() {
        return solveCellsUniqueValue(SOLVE_CELLS_UNIQUE_VALUE_ROWS);
    }

    private int ScanRowsWithOneBlankCell(){
        return -1;
    }

    private int ScanColsWithOneBlankCell(){
        return -1;
    }

    private int ScanRegionsWithOneBlankCell(){
        return -1;
    }

    // ----- PUBLIC -----

    public boolean solve(){
        while (this.mPuzzle.getUnsolvedCells() > 0){
            if (solveCellsWithOnePossibility()) {
                continue;
            }
            if (solveRowsUniqueValue()) {
                continue;
            }
            if (solveColsUniqueValue()) {
                continue;
            }
            if (solveRegionsUniqueValue()){
                continue;
            }

            break;
        }

        return (this.mPuzzle.getUnsolvedCells() == 0);
    }

    public int hint(){
        int foundHint = -1;
        
        foundHint = ScanRowsWithOneBlankCell();

        if (foundHint >= 0){
            return foundHint;
        }

        foundHint = ScanColsWithOneBlankCell();

        if (foundHint >= 0){
            return foundHint;
        }

        foundHint = ScanRegionsWithOneBlankCell();

        if (foundHint >= 0){
            return foundHint;
        }

        return foundHint;
    }

    // ----- CONSTRUCTOR -----

    public SudokuBot(SudokuPuzzle puzzle){
        this.mPuzzle = puzzle;
    }
}
